#include <ne.h>
#include <system/GlutApplication.h>

int Width = 1280;
int Height = 720;
float SkyTextureSize = 512.0f;
float SkyBoxSize = 1024.0f;
size_t billboardCount = 10000;

std::string skyBoxImageMap[6] = {"../image/skybox/stevecube_RT.jpg",
                                 "../image/skybox/stevecube_LF.jpg",
                                 "../image/skybox/stevecube_UP.jpg",
                                 "../image/skybox/stevecube_DN.jpg",
                                 "../image/skybox/stevecube_BK.jpg",
                                 "../image/skybox/stevecube_FR.jpg"};

struct Statistic
{
    ne::uint32_t counter;
    ne::real totalTime;
    ne::real totalTimeT;
    ne::real renderTime;
    ne::real renderTimeT;
    ne::real tickTime;
    ne::real tickTimeT;
};

Statistic gameStatistic;

ne::Engine *pEngine = 0;
ne::Timer timer;
ne::Camera *camera = 0;
ne::Viewport *viewport = 0;
ne::GlutApplication *pApplication = 0;
ne::ParticleSystem *pParticleSystem = 0;
ne::ParticleEmitter *pEmitter = 0;
ne::BillboardSet *pBillboardSet = 0;
ne::TexturePtr pTexture;
ne::TexturePtr pSkyTexture;
ne::Vector2 mousePosition;
bool mouseLeftOn = false;
bool mouseRightOn = false;
bool showAABB = false;

int sampleLocation = 0;

void mouseFunc(int button, int state, int x, int y);
void motionFunc(int x, int y);
void loop(void);
void redraw(void);
void drawFPS(void);
void resize(int width, int height);
void init();

int main(int argc, char *argv[]) /* I - Command-line arguments */
{
    pApplication = new ne::GlutApplication();//(argc, argv);
    glutReshapeFunc(resize);
    glutDisplayFunc(loop);
    glutMouseFunc(mouseFunc);
    glutMotionFunc(motionFunc);

    pEngine = pApplication->engine;
    if (!pEngine || !pEngine->isReady())
    {
        delete pApplication;
        return 0;
    }

    init();

    glutMainLoop();

    delete pApplication;

    return 0;
}

void init()
{
    gameStatistic.counter = 0;
    gameStatistic.totalTime = 0.0f;
    gameStatistic.totalTimeT = 0.0f;
    gameStatistic.renderTime = 0.0f;
    gameStatistic.renderTimeT = 0.0f;
    gameStatistic.tickTime = 0.0f;
    gameStatistic.tickTimeT = 0.0f;

    camera = new ne::Camera();
    viewport = new ne::Viewport(camera);
    camera->setPosition(ne::Vector3::UNIT_Z * 50.0f);

    glClearColor(0, 0, 0, 0);
    glClearDepth(1);
    pEngine->getRenderSystem()->setBlendEnable(true);
    pEngine->getRenderSystem()->setBlendFunction(ne::BF_SRC_ALPHA, ne::BF_ONE_MINUS_SRC_ALPHA);

    pEngine->getRenderSystem()->setAlphaTestFunc(ne::CF_GREATER, 0.0f);

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    glCullFace(GL_BACK);
    glEnable(GL_CULL_FACE);

    glDisable(GL_LIGHTING);

    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

    glShadeModel(GL_SMOOTH);
    glEnable(GL_POINT_SMOOTH);
    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_POLYGON_SMOOTH);
    glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

    camera->setFarClipDistance(1800.0f);

    ne::ImagePtr pImage;
    pSkyTexture = pEngine->getTextureManager()->createTexture("SkyBox", "SkyBox", ne::TT_CUBE_MAP, ne::TF_RGB, ne::PT_UNSIGNED_BYTE, SkyTextureSize, SkyTextureSize);
    for (size_t i=0; i<6; ++i)
    {
        pImage = pEngine->getImageManager()->create("SkyBoxImage", skyBoxImageMap[i]);
        pImage->load();
        pSkyTexture->loadImageForCubeMap(ne::TextureCubeMapFace(ne::TCMF_POSITIVE_X + i), *pImage);
        pEngine->getImageManager()->removeByPtr(pImage);
    }
    pSkyTexture->createRenderTexture();

    pParticleSystem = new ne::ParticleSystem();
    //pParticleSystem->setSortEnable(true);
    //pParticleSystem->setSortMode(ne::SM_DISTANCE);
    pParticleSystem->setParticlePoolSize(billboardCount);
    pParticleSystem->createAffector("ParticleFadeAffector");
    pParticleSystem->createAffector("ParticleGravityAffector");
    pEmitter = pParticleSystem->createEmitter();//pParticleSystem->createEmittedEmitter();
    if (pEmitter)
    {
        pEmitter->direction = ne::Vector3::UNIT_Y;
        pEmitter->scale = 0.5f * ne::Vector3::ONE;
        pEmitter->setEmissionRate(ne::real(billboardCount) / 2.0f);
        //pEmitter->setDuration(10.0f);
        //pEmitter->setRepeatDelay(2.0f);
        //pEmitter->setAngle(0.5f * ne::Math::HALF_PI, 1.5f * ne::Math::HALF_PI);
        pEmitter->setVelocity(0.0f, 14.0f);
        pEmitter->setZoom(1.0f);
        pEmitter->setColorRange(ne::Color::YELLOW, ne::Color(1.0f, 0.1f, 0.1f, 1.0f));
        pEmitter->setLifetime(2.0f);
    }

    pTexture = pEngine->getTextureManager()->createTextureFromFile("billboard", "../image/billboard.png", ne::TF_RGBA);
    pTexture->load();
    pTexture->setNumMipmap(8);
    pTexture->createRenderTexture();

    pBillboardSet = new ne::BillboardSet();
    pBillboardSet->setBillboardType(ne::BBT_POINT);
    pBillboardSet->setUseOwnSize(true);
    pBillboardSet->setSortEnable(true);
    pBillboardSet->setCullIndividually(true);
    pBillboardSet->setBillboardPoolSize(billboardCount);
    pBillboardSet->setSize(0.5f, 0.5f);
    //pBillboardSet->rotate(ne::Vector3::UNIT_X, ne::Math::HALF_PI, ne::TS_WORLD);
    pBillboardSet->update();
    for (size_t i=0; i<billboardCount; ++i)
    {
        pBillboardSet->createBillboard();
    }
}

void mouseFunc(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
    {
        mousePosition.x = x;
        mousePosition.y = y;
        mouseLeftOn = true;
    }
    else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
    {
        mouseLeftOn = false;
    }
    if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
    {
        mousePosition.x = x;
        mousePosition.y = y;
        mouseRightOn = true;
    }
    else if (button == GLUT_RIGHT_BUTTON && state == GLUT_UP)
    {
        mouseRightOn = false;
    }
}

void motionFunc(int x, int y)
{
    if (mouseLeftOn)
    {
        ne::Ray outRay;
        viewport->getScreenToWorldRay(ne::real(x), ne::real(y), outRay);
        ne::Plane p(ne::Vector3::UNIT_Z, 0.0f);
        ne::real t = -1.0f;
        if (outRay.trace(p, t))
        {
            ne::Vector3 point = outRay.getPoint(t);
            pEmitter->position = point;
            pParticleSystem->setPosition(-point);
        }
    }
    if (mouseRightOn)
    {
        float dx = x - mousePosition.x;
        float dy = y - mousePosition.y;
        camera->yaw(-0.01f * dx * ne::Math::PI / 36, ne::TS_WORLD);
        camera->pitch(-0.01f * dy * ne::Math::PI / 36);
        mousePosition.x = x;
        mousePosition.y = y;
    }
}

void loop(void)
{
    ne::Keyboard *pKeyboard = pEngine->getKeyboard();
    ne::real speed = 0.1f;
    if (pKeyboard->isKeyDown(ne::KK_F12))
    {
        speed = 1.0f;
    }
    if (pKeyboard->isKeyDown(ne::KK_W))
    {
        camera->translate(speed * ne::Vector3::NEGATIVE_UNIT_Z, ne::TS_LOCAL);
    }
    if (pKeyboard->isKeyDown(ne::KK_S))
    {
        camera->translate(speed * ne::Vector3::UNIT_Z, ne::TS_LOCAL);
    }
    if (pKeyboard->isKeyDown(ne::KK_A))
    {
        camera->translate(speed * ne::Vector3::NEGATIVE_UNIT_X, ne::TS_LOCAL);
    }
    if (pKeyboard->isKeyDown(ne::KK_D))
    {
        camera->translate(speed * ne::Vector3::UNIT_X, ne::TS_LOCAL);
    }
    if (pKeyboard->isKeyDown(ne::KK_Z))
    {
        camera->translate(speed * ne::Vector3::NEGATIVE_UNIT_Y, ne::TS_PARENT);
    }
    if (pKeyboard->isKeyDown(ne::KK_Q))
    {
        camera->translate(speed * ne::Vector3::UNIT_Y, ne::TS_PARENT);
    }
    if (pKeyboard->isKeyDown(ne::KK_NUMPAD1))
    {
        pParticleSystem->play();
    }
    if (pKeyboard->isKeyDown(ne::KK_NUMPAD2))
    {
        pParticleSystem->pause(false);
    }
    if (pKeyboard->isKeyDown(ne::KK_NUMPAD3))
    {
        pParticleSystem->pause();
    }
    if (pKeyboard->isKeyDown(ne::KK_NUMPAD4))
    {
        pParticleSystem->stop();
    }
    if (pKeyboard->isKeyDown(ne::KK_NUMPAD5))
    {
        pParticleSystem->stop(true);
    }

    if (pKeyboard->isKeyDown(ne::KK_F1))
    {
        pBillboardSet->setPointRendering(false);
        pBillboardSet->setBillboardType(ne::BBT_POINT);
    }
    if (pKeyboard->isKeyDown(ne::KK_F2))
    {
        pBillboardSet->setPointRendering(false);
        pBillboardSet->setBillboardType(ne::BBT_ORIENTED_COMMON);
    }
    if (pKeyboard->isKeyDown(ne::KK_F3))
    {
        pBillboardSet->setPointRendering(false);
        pBillboardSet->setBillboardType(ne::BBT_ORIENTED_SELF);
    }
    if (pKeyboard->isKeyDown(ne::KK_F4))
    {
        pBillboardSet->setPointRendering(false);
        pBillboardSet->setBillboardType(ne::BBT_PERPENDICULAR_COMMON);
    }
    if (pKeyboard->isKeyDown(ne::KK_F5))
    {
        pBillboardSet->setPointRendering(false);
        pBillboardSet->setBillboardType(ne::BBT_PERPENDICULAR_SELF);
    }
    if (pKeyboard->isKeyDown(ne::KK_F6))
    {
        pBillboardSet->setPointRendering(true);
    }
    if (pKeyboard->isKeyDown(ne::KK_F))
    {
        pBillboardSet->setAccurateFacing(!pBillboardSet->isAccurateFacing());
    }

    if (pKeyboard->isKeyDown(ne::KK_C))
    {
        pBillboardSet->setUseColor(!pBillboardSet->isUseColor());
    }
    if (pKeyboard->isKeyDown(ne::KK_T))
    {
        pBillboardSet->setUseTexCoord(!pBillboardSet->isUseTexCoord());
    }

    if (pKeyboard->isKeyDown(ne::KK_B))
    {
        showAABB = !showAABB;
    }
    if (pKeyboard->isKeyDown(ne::KK_ESC))
    {
        pTexture.setNull();
        pSkyTexture.setNull();
        exit(0);
    }

    ne::real interval = timer.getIntervalMilliseconds();
    gameStatistic.totalTimeT += interval;
    ne::Log::Out("%f", interval);

    ne::real now = timer.getMilliseconds();
    pParticleSystem->tick(0.001f * interval);
    pParticleSystem->updateParticles(camera);

    pBillboardSet->clearBillboards();

    ne::ParticleList &particleList = pParticleSystem->getParticleList();
    ne::ParticleListIterator it, iend;
    iend = particleList.end();

    for (it=particleList.begin(); it!=iend; ++it)
    {
        ne::Particle *pParticle = *it;
        if (pParticle)
        {
            ne::Billboard *pBillboard = pBillboardSet->createBillboard();
            if (pBillboard)
            {
                pBillboard->position = pParticle->position;
                pBillboard->axis = pParticle->direction;
                pBillboard->width = pParticle->scale.x;
                pBillboard->height = pParticle->scale.y;
                pBillboard->color = pParticle->color;
            }
        }
    }
    pBillboardSet->updateBillboards(camera);

    ne::real now1 = timer.getMilliseconds();

    gameStatistic.tickTimeT += now1 - now;

    redraw();

    gameStatistic.renderTimeT += timer.getMilliseconds() - now1;

    ++gameStatistic.counter;
    if (gameStatistic.totalTimeT > 1000)
    {
        gameStatistic.tickTime = gameStatistic.tickTimeT / gameStatistic.counter;
        gameStatistic.tickTimeT = 0;
        gameStatistic.renderTime = gameStatistic.renderTimeT / gameStatistic.counter;
        gameStatistic.renderTimeT = 0;
        gameStatistic.totalTime = gameStatistic.totalTimeT / gameStatistic.counter;
        gameStatistic.totalTimeT = 0;
        gameStatistic.counter = 0;
    }
}

void drawSky(void)
{
    glDisable(GL_DEPTH_TEST);
    glDepthMask(GL_FALSE);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(pSkyTexture->getType(), pSkyTexture->getSource());
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glEnable(pSkyTexture->getType());

    glBegin(GL_QUADS);
        glTexCoord3f(-1.0f, 1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, SkyBoxSize);
        glTexCoord3f(-1.0f, -1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, SkyBoxSize);
        glTexCoord3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(-1.0f, 1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, -SkyBoxSize);

        glTexCoord3f(1.0f, 1.0f, 1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, SkyBoxSize);
        glTexCoord3f(1.0f, 1.0f, -1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, -1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, 1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, SkyBoxSize);

        glTexCoord3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(-1.0f, -1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, 1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, -1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, -SkyBoxSize);

        glTexCoord3f(-1.0f, 1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(1.0f, 1.0f, -1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(1.0f, 1.0f, 1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, SkyBoxSize);
        glTexCoord3f(-1.0f, 1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, SkyBoxSize);

        glTexCoord3f(1.0f, 1.0f, -1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(-1.0f, 1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, -1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, -SkyBoxSize);

        glTexCoord3f(1.0f, 1.0f, 1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, 1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, SkyBoxSize);
        glTexCoord3f(-1.0f, -1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, SkyBoxSize);
        glTexCoord3f(-1.0f, 1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, SkyBoxSize);
    glEnd();
    glDisable(pSkyTexture->getType());

    glEnable(GL_DEPTH_TEST);
    glDepthMask(GL_TRUE);
}

void drawMesh(void)
{
    glNormal3f(0.0f, 1.0f, 0.0f);
    glBegin(GL_QUADS);
        glColor4f(0.2f, 0.2f, 0.2f, 1.0f);
        glTexCoord2f(0.0f, 1.0f);
        glVertex3f(-100, -100, 100);
        glTexCoord2f(0.0f, 0.0f);
        glVertex3f(100, -100, 100);
        glTexCoord2f(1.0f, 0.0f);
        glVertex3f(100, -100, -100);
        glTexCoord2f(1.0f, 1.0f);
        glVertex3f(-100, -100, -100);
    glEnd();

    size_t particleCount = pParticleSystem->getParticleNum();
    if (particleCount > 0)
    {
        pEngine->getRenderSystem()->setBlendFunction(ne::BF_SRC_ALPHA, ne::BF_ONE);
        pEngine->getRenderSystem()->setAlphaTestEnable(true);

        ne::VertexDataPtr vertexData = pBillboardSet->getVertexData();
        ne::IndexDataPtr indexData = pBillboardSet->getIndexData();
        ne::VertexAttributePtr va;

        va = vertexData->getAttribute(ne::VAS_POSITION);
        if (va.notNull())
        {
            glBindBuffer(GL_ARRAY_BUFFER, va->getSource());
            glVertexPointer(va->getGLDataCount(), va->getGLDataType(), va->getStride(), va->getOffsetPointer());
            glEnableClientState(GL_VERTEX_ARRAY);
        }

        va = vertexData->getAttribute(ne::VAS_COLOR);
        if (va.notNull())
        {
            glBindBuffer(GL_ARRAY_BUFFER, va->getSource());
            glColorPointer(va->getGLDataCount(), va->getGLDataType(), va->getStride(), va->getOffsetPointer());
            glEnableClientState(GL_COLOR_ARRAY);
        }

        size_t renderCount = std::min(pBillboardSet->getVisibleBillboardNum(), pParticleSystem->getParticleNum());

        bool isPointRendering = pBillboardSet->isPointRendering();
        if (isPointRendering)
        {
            glPointSize(4.0f);
            glDisable(pTexture->getType());
            glDrawArrays(GL_POINTS, 0, renderCount);
            glPointSize(1.0f);
        }
        else
        {
            va = vertexData->getAttribute(ne::VAS_TEXTURE_COORD);
            if (va.notNull())
            {
                glBindTexture(pTexture->getType(), pTexture->getSource());
                glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                glEnable(pTexture->getType());

                glClientActiveTexture(GL_TEXTURE0);
                glBindBuffer(GL_ARRAY_BUFFER, va->getSource());
                glTexCoordPointer(va->getGLDataCount(), va->getGLDataType(), va->getStride(), va->getOffsetPointer());
                glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            }

            if (indexData.notNull())
            {
                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexData->getSource());
                glDrawElements(indexData->getPrimitiveType(), 6 * renderCount, indexData->getGLDataType(), indexData->getOffsetPointer());
            }
        }

        glDisable(pTexture->getType());
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);

        pEngine->getRenderSystem()->setAlphaTestEnable(false);

        if (showAABB)
        {
            glDisable(GL_DEPTH_TEST);
            glDepthMask(GL_FALSE);

            if (isPointRendering)
            {
                glDrawArrays(GL_POINTS, 0, renderCount);
            }
            else
            {
                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                glDrawElements(GL_TRIANGLES, 6 * renderCount, GL_UNSIGNED_INT, 0);
            }

            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            glEnable(GL_DEPTH_TEST);
            glDepthMask(GL_TRUE);
        }
        glDisableClientState(GL_COLOR_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
}

void redraw(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf(camera->getViewMatrix().ptr());

    glPushMatrix();
        ne::Vector3 pos = camera->getPosition();
        glTranslatef(pos.x, pos.y, pos.z);
        drawSky();
    glPopMatrix();

    if (showAABB)
    {
        if (pParticleSystem->getAABB().isFinite())
        {
            const ne::Vector3 *corners = pParticleSystem->getAABB().getCorners();
            if (corners)
            {
                glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
                glBegin(GL_LINES);
                    glVertex3fv(corners[0].ptr());
                    glVertex3fv(corners[1].ptr());
                    glVertex3fv(corners[1].ptr());
                    glVertex3fv(corners[2].ptr());
                    glVertex3fv(corners[2].ptr());
                    glVertex3fv(corners[3].ptr());
                    glVertex3fv(corners[3].ptr());
                    glVertex3fv(corners[0].ptr());

                    glVertex3fv(corners[4].ptr());
                    glVertex3fv(corners[5].ptr());
                    glVertex3fv(corners[5].ptr());
                    glVertex3fv(corners[6].ptr());
                    glVertex3fv(corners[6].ptr());
                    glVertex3fv(corners[7].ptr());
                    glVertex3fv(corners[7].ptr());
                    glVertex3fv(corners[4].ptr());

                    glVertex3fv(corners[0].ptr());
                    glVertex3fv(corners[6].ptr());
                    glVertex3fv(corners[1].ptr());
                    glVertex3fv(corners[5].ptr());
                    glVertex3fv(corners[2].ptr());
                    glVertex3fv(corners[4].ptr());
                    glVertex3fv(corners[3].ptr());
                    glVertex3fv(corners[7].ptr());
                glEnd();
            }
        }
    }

    glDisable(GL_CULL_FACE);
    drawMesh();
    glEnable(GL_CULL_FACE);

    pEngine->getRenderSystem()->setBlendFunction(ne::BF_SRC_ALPHA, ne::BF_ONE_MINUS_SRC_ALPHA);

    drawFPS();

    glutSwapBuffers();
    glutPostRedisplay();
}

void resize(int width, int height)
{
    /* Save the new width and height */
    Width  = width;
    Height = height;

    /* Reset the viewport->.. */
    glViewport(0, 0, width, height);
    viewport->setPosition(0, 0);
    viewport->resize(width, height);

    glMatrixMode(GL_PROJECTION);
    const ne::Matrix4 &projectionMatrix = camera->getProjectionMatrix();
    glLoadMatrixf(projectionMatrix.ptr());
    glMatrixMode(GL_MODELVIEW);
}

void drawFPS()
{
    glClear(GL_DEPTH_BUFFER_BIT);

    //Set matrices for ortho
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glOrtho(0.0f, viewport->getWidth(),
            0.0f, viewport->getHeight(),
            1.0f, -1.0f);

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	//Print text
    ne::FontPtr font = pEngine->getFont();
    ne::Vector3 pos(Width - 200.0f, 20.0f, 0.0f);
    float w = font->getFontWidth();
    float h = font->getFontHeight();

    char fpsString[32];
	sprintf(fpsString, "FPS : %.2f", 1000.0 / gameStatistic.totalTime);

    font->renderText2D(fpsString, pos, 32.0f*w, h, ne::Color(1.0f, 0.0f, 0.0f, 1.0f));

    pos.y += 12.0f;
    char parString[32];
    sprintf(parString, "P Pool Size : %d", (int)pParticleSystem->getParticlePoolSize());
    font->renderText2D(parString, pos, 32.0f*w, h, ne::Color(0.0f, 0.8f, 1.0f, 1.0f));
    pos.y += 12.0f;
    sprintf(parString, "Active B : %d", (int)pBillboardSet->getBillboardNum());
    font->renderText2D(parString, pos, 32.0f*w, h, ne::Color(0.0f, 0.8f, 1.0f, 1.0f));
    pos.y += 12.0f;
    sprintf(parString, "Visible B : %d", (int)pBillboardSet->getVisibleBillboardNum());
    font->renderText2D(parString, pos, 32.0f*w, h, ne::Color(0.0f, 0.8f, 1.0f, 1.0f));
    pos.y += 12.0f;
	sprintf(parString, "Active P : %d", (int)pParticleSystem->getParticleNum());
    font->renderText2D(parString, pos, 32.0f*w, h, ne::Color(0.0f, 0.8f, 1.0f, 1.0f));

    pos.y += 24.0f;
	sprintf(parString, "Total : %.2f", gameStatistic.totalTime);
    font->renderText2D(parString, pos, 32.0f*w, h, ne::Color(1.0f, 0.5f, 0.0f, 1.0f));
    pos.y += 12.0f;
	sprintf(parString, "Render : %.2f", gameStatistic.renderTime);
    font->renderText2D(parString, pos, 32.0f*w, h, ne::Color(1.0f, 0.5f, 0.0f, 1.0f));
    pos.y += 12.0f;
	sprintf(parString, "Tick : %.2f", gameStatistic.tickTime);
    font->renderText2D(parString, pos, 32.0f*w, h, ne::Color(1.0f, 0.5f, 0.0f, 1.0f));

    //reset matrices
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
}

